/**
 * @class 宇宙控制器
 * @author DuskyHuang 圣子
 * @description 核心逻辑，主要负责视野区域的切换控制。
*/

import 'default-passive-events';
import { _decorator, Node, Camera, GeometryRenderer, Vec3 } from 'cc';
import { Controller, core, utils } from 'cfw';
import { cosmos, CosmosProxy, planetary, visit } from 'proxies';
import { UI, View } from 'notifications';
import { Res, SceneGraph, TransformEnum, TRANS_2_PLN, TRANS_2_SAT, TRANS_2_SUN, TRANS_INTER } from 'const';
import { SolarSystem, PlanetarySystem, Dsm, SpecificSystem, StellarGouldBelt, ICelestialSpecific } from 'prefabs';
import { RelativeCamera } from '../components/relative-camera';
import { RelativeScalar } from '../components/relative-scalar';
import { calcTransformParams } from 'helpers';
const { group, findComp } = utils;
const { ccclass } = _decorator;
@ccclass('cocosmos.CosmosController')
export class CosmosController extends Controller {

    @group('Views', '深空相机', Camera) public deepSkyCamera: Camera = null;
    @group('Views', '相对相机', Camera) public mainCamera: Camera = null;

    public get deepRenderer(): GeometryRenderer { return this.deepSkyCamera?.camera?.geometryRenderer; }
    public get mainRenderer(): GeometryRenderer { return this.mainCamera?.camera?.geometryRenderer; }

    public get center(): Node { return this.node; }
    public get scalars(): RelativeScalar[] { return this.getComponentsInChildren(RelativeScalar); }

    protected onLoad(): void { this._init()._listen(); }

    protected start(): void {
        this.mainCamera?.camera?.initGeometryRenderer();
        this.deepSkyCamera?.camera?.initGeometryRenderer();
        this._solar.sun?.loadReference(this._stellar.sun);
    }

    protected update(dt: time.sec): void {
        this._relCam.autoAA2nd = cosmos.autoAA2nd;
        if (!this._transforming && isNum(this._autoPow10)) this.notify(UI.Zoom, this._autoPow10 * dt);
        this._relCam.interactable = !this._looking && !this._locked;
    }

    protected onZoomStart(): void {
        this._transforming = true;
        this._loadTransMediator();
        this._loadTransTarget();
        const [bv, az, sign] = calcTransformParams(visit.transInfo, this._stellar.sun.node);
        const { pow10 } = cosmos;
        // this._relCam.interactable = false;
        this._looking = true;
        this._relCam.scrollSmoothing = 1;
        this._relCam.azimuthAngle = az;
        this._relCam.heightAngle = sign * TRANS_HIGHT;
        cosmos.zoomToBy(Math.max(pow10, bv), TRANS_SPEED_ZOOM_UP, false, Transform.ZOOM_OVER);
    }

    protected onZoomOver(): void {
        this._checkUniqueSystemReload();
        this._startFacingTo();
    }

    protected onAngleChange(az: num.deg, ha: num.deg): void {
        this._relCam.azimuthAngle = az;
        this._relCam.heightAngle = ha;
    }

    protected onLensPanelParams(params: LensPanelParams): void {
        const { lock, az, ha, pow10, haRange } = params;
        if (isNum(az) || isNum(ha)) {
            this._relCam.startAutoRotating(az ?? 0, ha ?? 0, null, haRange);
        } else {
            this._relCam.stopAutoRotating();
        }
        this._autoPow10 = pow10 ?? null;
        this._locked = lock;
    }

    private _init(): this {
        this._planetary = findComp(PlanetarySystem, this, PLANETARY);
        this._solar = findComp(SolarSystem, this, SOLAR);
        this._stellar = findComp(StellarGouldBelt, this, STELLAR);
        this._specific = findComp(SpecificSystem, this, SPECIFIC);
        this._relCam = this.mainCamera.getComponent(RelativeCamera);

        return this;
    }

    private _listen(): this {
        this.on(Transform.ZOOM_START, this.onZoomStart, 99);
        this.on(Transform.ZOOM_OVER, this.onZoomOver);
        this.on(Transform.VISIT_OVER, () => this._transforming = false);
        this.on(Lens.ANGLE_CHANGE, this.onAngleChange);
        this.on(Lens.ANGLE_ADDED, this.onAngleAdded);
        this.on(UI.LENS_PARAMS, this.onLensPanelParams);
        return this;
    }

    protected onAngleAdded(addAz: num.deg, ha?: num.deg): void {
        this._relCam.azimuthAngle += addAz;
        if (isNum(ha)) this._relCam.heightAngle = ha;
    }

    private _checkUniqueSystemReload(): void {
        if (visit.transInfo.transEnum === TransformEnum.PLN_2_PLN) {
            this._planetary.freeSystem();
            this._planetary.loadSystem();
        }
        if (visit.transInfo.transEnum === TransformEnum.SPC_2_SPC) {
            this._specific.loadStellarTargetAndSync();
        }
    }

    protected async _startFacingTo(): Promise<void> {
        const target = visit.transInfo.target;
        const dist = Vec3.distance(this.mainCamera.node.worldPosition, target.node.worldPosition);
        const add: pow10 = Math.log10(dist / this._relCam.fixedSphereRadius);
        let facingTo = target.node;
        if (visit.transInfo.stellar) {
            facingTo = (target as ICelestialSpecific).refer;
            // facingTo = visit.transInfo.stellar.refers.to; // 好像一致
        }
        await this._relCam.facingTo(facingTo, 1, () => {
            this._freeLastSystem();
            // this._relCam.interactable = true;
            this._looking = false;
            this._relCam.scrollSmoothing = 0.5;
            cosmos.addPow10(add);
            visit.visit(target);
            cosmos.zoomToBy(visit.point ?? target.suggest, TRANS_SPEED_ZOOM_IN, Math.randomBool(), Transform.VISIT_OVER);
        });  // Camera position was changed after this!
    }

    private _freeLastSystem(): void {
        const type = visit.transInfo.transEnum;
        const interstellar = TRANS_INTER.contains(type);
        const toSun = TRANS_2_SUN.contains(type);
        const toPln = TRANS_2_PLN.contains(type);
        const toSat = TRANS_2_SAT.contains(type);
        if (interstellar || toSun) this._planetary.freeSystem();
        if (toPln || toSun || toSat) this._specific.freeSystem();
    }

    private _loadTransMediator(): void {
        const { name, transEnum } = visit.transInfo;
        if (transEnum === TransformEnum.SUN_2_SAT) {
            const belongs = core.res.getConfigItem<dsmConfig>(PLANETARY_SATELLITES, name)?.belongs ?? '';
            const mediator = this._solar.getCelestial(belongs, 'planet') ?? this._solar.getCelestial(belongs, 'dwarf');
            if (!mediator) console.error('[loadTransMediator]', 'sun2sat no mediator!', name);
            visit.transInfo.mediator = mediator;
        } else {
            // TODO From Stellar ...
        }
    }

    private _loadTransTarget(): void {
        const { name, transEnum, into } = visit.transInfo;
        if (TRANS_INTER.contains(transEnum)) {
            this._specific.loadStellarTargetAndSync(transEnum === TransformEnum.SPC_2_SPC); // 这儿仿照Planet延迟加载
        } else if (TRANS_2_PLN.contains(transEnum)) {
            visit.transInfo.target = this._solar.getCelestial(name, into as Extract<orbit, 'planet' | 'dwarf' | 'minor' | 'comet' | 'belts'>);
            if (transEnum === TransformEnum.SUN_2_PLN) this._planetary.loadSystem();
        } else if (TRANS_2_SUN.contains(transEnum)) {
            visit.transInfo.target = this._solar.getCelestial(name);
        } else if (TRANS_2_SAT.contains(transEnum)) {
            const sun2sat = transEnum === TransformEnum.SUN_2_SAT;
            sun2sat && this._planetary.loadSystem();
            visit.transInfo.target = this._planetary.getSatellite(name as satellite);
            sun2sat && planetary.reloadOrbiterParams(visit.transInfo.target as Dsm);
        }
        Function.invoke(visit?.transInfo.target?.preloadRes, visit.transInfo.target);
    }

    private _transforming: boolean = false;
    private _looking: boolean = false;
    private _autoPow10: pow10 | null = null;
    private _locked: boolean = false;
    private _relCam: RelativeCamera
    private _solar: SolarSystem;
    private _stellar: StellarGouldBelt;
    private _planetary: PlanetarySystem;
    private _specific: SpecificSystem;
}

const { TRANS_HIGHT, TRANS_SPEED_ZOOM_UP, TRANS_SPEED_ZOOM_IN } = CosmosProxy;
const { SOLAR, PLANETARY, STELLAR, SPECIFIC } = SceneGraph;
const { Transform, Lens } = View;
const { PLANETARY_SATELLITES, VISITABLE_STARS } = Res.Configs;